package com.liyiwei.redislock.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class RedisRLockImpl implements RLock {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    @Qualifier("acquireLockScipt")
    private DefaultRedisScript acquireLockScipt;

    @Autowired
    @Qualifier("unLockScipt")
    private DefaultRedisScript unLockScipt;

    /**
     * redis分布式锁前缀
     */
    private static final String DEFAULT_REDIS_LOCK_KEY = "redis:rlock:";

    /**
     * 锁过期时间
     */
    private static long  RELEASE_TIME = 100l;

    /**
     * 超时时间（单位 秒）
     */
    private static Integer  OVER_TIME = 4;

    public  String LOCKHASHKEY = "";

    /**
     * 默认的锁过期时间和重试时间（多长时间获取不到返回）
     * @param lockName 锁的名字
     * @param key key对应的Hash结构的key
     * @return 锁的标识
     */
    @Override
    public String tryLockDefaultByTime(String lockName,String key){
        return tryLock(lockName,key,RELEASE_TIME,OVER_TIME);
    }

    @Override
    public String tryLockDefaultByTime(String lockName){
        return tryLock(lockName,null,RELEASE_TIME,OVER_TIME);
    }

    @Override
    public String tryLock(String lockName,String key, long releaseTime,Integer overTime) {
        lockName = DEFAULT_REDIS_LOCK_KEY + lockName;
        //存入线程的前缀信息，防止和其他的JVM线程发生冲突
        String threadFlag = UUID.randomUUID().toString();
        if (key == null){
            key = threadFlag + Thread.currentThread().getId();
        }
        long acquireLockTime = System.currentTimeMillis();
        do {
            try {
                //开始加锁
                Boolean lock = lock(lockName, releaseTime, key);
                if (lock){
                    LOCKHASHKEY = key;
                    return key;
                }else{
//                    log.info("获取分布式锁失败，休眠一秒后继续获取");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }while (System.currentTimeMillis() - acquireLockTime < overTime*1000);
        throw new RuntimeException("获取锁失败");
    }

    @Override
    public void unLock(String lockName, String key) {
        unlock(lockName,key);
    }


    private Boolean unlock(String lockName,String key){
        Long result = (Long) redisTemplate.execute(unLockScipt, Collections.singletonList(DEFAULT_REDIS_LOCK_KEY + lockName), key);
        if (null == result){
            throw new RuntimeException("解锁脚本异常");
        }else if (-1L == result){
//            log.info("解锁完成");
            return true;
        }else if (0L == result){
            return false;
        }else{
//            log.info("还需要解锁" + result + "次");
            return false;
        }
    }

    private Boolean lock(String lockName, Long releaseTime,String hashKey){
        List<String> paramList = new ArrayList<>();
        paramList.add(lockName);
        Long result = (Long) redisTemplate.execute(acquireLockScipt, paramList,hashKey,releaseTime);
        if (null == result){
            throw new RuntimeException("获取锁异常");
        }else if (1L == result ){
            return true;
        }else{
            return false;
        }
    }
}
